#include "assembleDna.h"
#include <iostream>

using namespace std;



int assembleDna(DnaSeq * in1, DnaSeq * in2, DnaSeq * out)
{
	bool equal = true;

	// test de longueur
	unsigned int len1 = in1->len;
	unsigned int len2 = in2->len;
	if (len2 > len1+1)
		return false;

	// test partie commune
	unsigned int debut = len1 - (len2-1);
	unsigned int it2 = 0;
	char tmp1;
	char tmp2;

	for (unsigned int it1 = debut; it1<len1; it1++)
	{
		tmp1 = (char)Nucl(GET_NUCLEOTIDE(in1->nucl[NUCL_ARRAY_POS(it1)/*char*/], NUCL_CHAR_POS(it1)/*nucl*/));
		tmp2 = (char)Nucl(GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(it2)/*char*/], NUCL_CHAR_POS(it2)/*nucl*/));
		if (tmp1 != tmp2)
			equal = false;
		it2++;
	}

	// insertion
	if (equal)
	{
		out->flag = 0;
		for (unsigned int i = 0; i< (len1%4 ? len1/4+1 : len1/4); i++) // copie par char
			out->nucl[i] = in1->nucl[i];
		out->len = len1;

		int insert = Nucl(GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(len2-1)], NUCL_CHAR_POS(len2-1)));
		out->nucl[NUCL_ARRAY_POS(out->len)] = SET_NUCLEOTIDE(out->nucl[NUCL_ARRAY_POS(out->len)], insert, NUCL_CHAR_POS(out->len));

		out->len++;
		in1->flag = 1;
		in2->flag = 1;
	}

	return equal;
}

// Assemble les séquences Adn de même longueur
int assembleDna2(DnaSeq * in1, DnaSeq * in2, DnaSeq * out)
{
	bool equal = true;

	// test de longueur
	unsigned int len1 = in1->len;
	unsigned int len2 = in2->len;
	if (len2 != len1)
		return false;

	// test partie commune
	unsigned int debut = len1 - (len2-1);
	unsigned int it2 = 0;
	int tmp1;
	int tmp2;

	for (unsigned int it1 = debut; it1<len1; it1++, it2++)
	{
		tmp1 = GET_NUCLEOTIDE(in1->nucl[NUCL_ARRAY_POS(it1)/*char*/], NUCL_CHAR_POS(it1)/*nucl*/);
		tmp2 = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(it2)/*char*/], NUCL_CHAR_POS(it2)/*nucl*/);
		if (tmp1 != tmp2) {
			equal = false;
			break;
		}
	}

	// insertion
	if (equal)
	{
		out->flag = 0;
		for (unsigned int i = 0; i< (len1%4 ? len1/4+1 : len1/4); i++) // copie par char
			out->nucl[i] = in1->nucl[i];

		out->len = len1;

		int insert = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(len2-1)], NUCL_CHAR_POS(len2-1));
		out->nucl[NUCL_ARRAY_POS(out->len)] = SET_NUCLEOTIDE(out->nucl[NUCL_ARRAY_POS(out->len)], insert, NUCL_CHAR_POS(out->len));

		out->len++;
		in1->flag = 1;
		in2->flag = 1;
	}

	return equal;
}

// Swap
int assembleDna3(DnaSeq * in1, DnaSeq * in2, DnaSeq * out)
{
	// test de longueur
	if ((in2->len) > (in1->len))
	{
		/* Swap lists */
		DnaSeq *tmp = in1;
		in1 = in2;
		in2 = tmp;
	}

	bool equal = true;
	unsigned int len1 = in1->len;
	unsigned int len2 = in2->len;

	// test partie commune
	unsigned int debut = len1 - (len2-1);
	unsigned int it2 = 0;
	int tmp1;
	int tmp2;

	for (unsigned int it1 = debut; it1<len1; it1++, it2++)
	{
		tmp1 = GET_NUCLEOTIDE(in1->nucl[NUCL_ARRAY_POS(it1)/*char*/], NUCL_CHAR_POS(it1)/*nucl*/);
		tmp2 = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(it2)/*char*/], NUCL_CHAR_POS(it2)/*nucl*/);
		if (tmp1 != tmp2) {
			equal = false;
			break;
		}
	}

	// insertion
	if (equal)
	{
		out->flag = 0;
		for (unsigned int i = 0; i< (len1%4 ? len1/4+1 : len1/4); i++) // copie par char
			out->nucl[i] = in1->nucl[i];

		out->len = len1;

		int insert = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(len2-1)], NUCL_CHAR_POS(len2-1));
		out->nucl[NUCL_ARRAY_POS(out->len)] = SET_NUCLEOTIDE(out->nucl[NUCL_ARRAY_POS(out->len)], insert, NUCL_CHAR_POS(out->len));

		out->len++;
#pragma omp critical
		{
			in1->flag = 1;
			in2->flag = 1;
		}
	}

	return equal;
}

/* Prototype of local function*/
int assemble(DnaSeq * in1, DnaSeq * in2, DnaSeq * out);

/* Assemble deux courtes séquences */
int assembleDna4(DnaSeq * in1, DnaSeq * in2, DnaSeq * out, int thres)
{
	// Ne pas comparer si les deux longueurs sont inférieurs au seuil
	if (((in1->len) <= thres) && ((in2->len) <= thres)) {
		return false;
	}

	bool match=false;

	// Essaye d'allonger in1 avec in2
	if ( (in1->len) >= (in2->len) )
	{
		match=assemble(in1, in2, out);
	}
	// Essaye d'allonger in2 avec in1, il ne peut y avoir au total un seul match
	if ( ((in2->len) >= (in1->len)) && !match )
	{
		match=assemble(in2, in1, out);
	}

	return match;
}

/* Essaye d'allonger in1 avec in2 */
int assemble(DnaSeq * in1, DnaSeq * in2, DnaSeq * out)
{
	bool equal = true;
	unsigned int len1 = in1->len;
	unsigned int len2 = in2->len;
	// test partie commune
	unsigned int debut = len1 - (len2-1);
	unsigned int it2 = 0;
	int tmp1;
	int tmp2;

	for (unsigned int it1 = debut; it1<len1; it1++, it2++)
	{
		tmp1 = GET_NUCLEOTIDE(in1->nucl[NUCL_ARRAY_POS(it1)/*char*/], NUCL_CHAR_POS(it1)/*nucl*/);
		tmp2 = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(it2)/*char*/], NUCL_CHAR_POS(it2)/*nucl*/);
		if (tmp1 != tmp2) {
			equal = false;
			break;
		}
	}

	// insertion
	if (equal)
	{
		out->flag = 0;
		for (unsigned int i = 0; i< (len1%4 ? len1/4+1 : len1/4); i++) // copie par char
			out->nucl[i] = in1->nucl[i];

		out->len = len1;

		int insert = GET_NUCLEOTIDE(in2->nucl[NUCL_ARRAY_POS(len2-1)], NUCL_CHAR_POS(len2-1));
		out->nucl[NUCL_ARRAY_POS(out->len)] = SET_NUCLEOTIDE(out->nucl[NUCL_ARRAY_POS(out->len)], insert, NUCL_CHAR_POS(out->len));

		out->len++;
#pragma omp critical
		{
			in1->flag = 1;
			in2->flag = 1;
		}
	}
	return equal;
}
